package com.dtease.agent.sync.extractor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dtease.agent.core.constants.BizConstants;
import com.dtease.agent.biz.agentstore.entity.BasToolsMetaDatabase;
import com.dtease.agent.biz.resource.dto.BasStorageDTO;
import com.dtease.agent.common.utils.JdbcUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description 抽取元数据的抽象基类，用于封装公共逻辑
 * @author chentl
 * @class AbstractMetadataExtractor
 * @package com.dtease.agent.metadata.sync.extractor
 * @since 2025/5/7 14:25
 */
@Slf4j
public abstract class AbstractMetadataExtractor implements IMetadataExtractor {

    // 每批处理的数据量
    private static final int BATCH_SIZE = 500;

    /**
     * 获取要查询的 SQL 语句（子类必须实现）
     *
     * @param storage 数据源信息
     * @return 查询 SQL
     */
    protected abstract String getQuerySql(BasStorageDTO storage);

    /**
     * 获取数据源连接对象（子类必须实现）
     *
     * @param storage 数据源信息
     * @return JDBC 数据源
     */
    protected abstract DataSource getDataSource(BasStorageDTO storage);

    @Override
    @Transactional("agentStoreTransactionManager")
    public void extractAndSave(BasStorageDTO storage) {
        try {
            String baseSql = getQuerySql(storage);
            DataSource dataSource = getDataSource(storage);

            deleteOldData(storage);

            AtomicInteger pageNum = new AtomicInteger(0);
            AtomicBoolean hasMore = new AtomicBoolean(true);

            while (hasMore.get()) {
                List<Entity> result = JdbcUtil.queryPage(dataSource, baseSql, pageNum.get(), BATCH_SIZE);
                if (CollUtil.isEmpty(result)) {
                    hasMore.set(false);
                    break;
                }

                List<BasToolsMetaDatabase> insertBatch = CollUtil.newArrayList();
                for (Entity row : result) {
                    BasToolsMetaDatabase meta = buildMetadata(row, storage);
                    insertBatch.add(meta);
                }

                saveBatch(insertBatch, storage);
                log.info("Saved metadata batch: {}", insertBatch.size());
                pageNum.getAndIncrement();
            }
        } catch (Exception e) {
            log.error("同步元数据失败，storageId={}", storage.getStorageId(), e);
            throw e;
        }
    }

    /**
     * 删除旧数据（可覆盖）
     *
     * @param storage 数据源信息
     */
    protected void deleteOldData(BasStorageDTO storage) {
        getToolsMetaDatabaseService().lambdaUpdate()
                .eq(BasToolsMetaDatabase::getStorageId, storage.getStorageId())
                .remove();
    }

    /**
     * 构造单条元数据记录（子类可重写以定制字段映射）
     *
     * @param row     查询结果的一行
     * @param storage 数据源信息
     * @return 元数据对象
     */
    protected BasToolsMetaDatabase buildMetadata(Entity row, BasStorageDTO storage) {
        BasToolsMetaDatabase meta = new BasToolsMetaDatabase();
        meta.setId(IdUtil.getSnowflakeNextIdStr());
        meta.setStorageId(storage.getStorageId());
        meta.setStorageName(storage.getStorageName());
        meta.setStorageType(storage.getStorageType().toString());
        meta.setDatabaseName(getDatabaseName(storage));
        meta.setTenantId(storage.getTenantId());
        meta.setWorkspaceId(storage.getWorkspaceId());

        meta.setInvalid(BizConstants.Numbers.ZERO_NUM);

        meta.setTableName(MapUtil.getStr(row, "tableName"));
        meta.setTableComment(MapUtil.getStr(row, "tableComment"));
        meta.setFieldName(MapUtil.getStr(row, "columnName"));
        meta.setFieldType(MapUtil.getStr(row, "columnType"));
        meta.setFieldComment(MapUtil.getStr(row, "columnComment"));
        return meta;
    }

    /**
     * 批量保存（可覆盖）
     *
     * @param batch   批量数据
     * @param storage 数据源信息
     */
    protected void saveBatch(List<BasToolsMetaDatabase> batch, BasStorageDTO storage) {
        getToolsMetaDatabaseService().saveBatch(batch);
    }

    /**
     * 获取数据库名称（子类可覆盖）
     *
     * @param storage 数据源信息
     * @return 数据库名
     */
    protected String getDatabaseName(BasStorageDTO storage) {
        return JSONUtil.parseObj(storage.getParameter()).getStr("dbName");
    }

    // 子类需注入 IToolsMetaDatabaseService
    protected abstract IService<BasToolsMetaDatabase> getToolsMetaDatabaseService();
}